#include "lv_port_rtt.h"

#include <rtthread.h>
#include <rtdevice.h>
#include <stdint.h>
#include <rthw.h>
#include <stdio.h>
#include "rt_lcd.h"
#include "../src/lv_hal/lv_hal_disp.h"

#ifndef FBDEV_NAME
#define FBDEV_NAME "hdmi"
#endif

#define TOUCH_NAME "touch"
//static struct rt_device_graphic_info lcd_info;

static struct fb_fix_screeninfo lcd_info;// = (struct fb_fix_screeninfo *)args;
static struct touch_screeninfo touch_info;
static struct fb_var_screeninfo lcd_var_info;// = (struct fb_var_screeninfo *)args;
static rt_uint8_t *fbp = 0;
static long int screensize = 0;
static rt_device_t lv_lcd;
static lv_color_int_t *rtt_fbp;
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * LV_VER_RES_MAX];                     /*Declare a buffer for 10 lines*/


static void touchpad_init(void);
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);

lv_indev_t * indev_touchpad;

void lv_port_indev_init(void)
{

    lv_indev_drv_t indev_drv;

    /*------------------
     * Touchpad
     * -----------------*/

    /*Initialize your touchpad if you have*/
    touchpad_init();

    /*Register a touchpad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = touchpad_read;
    indev_touchpad = lv_indev_drv_register(&indev_drv);

    /*------------------
     * Mouse
     * -----------------*/
}



/*------------------
 * Touchpad
 * -----------------*/
static uint32_t tx;
static uint32_t ty;
static uint32_t tstate;

/*Initialize your touchpad*/
static void touchpad_init(void)
{
    /*Your code comes here*/
    rt_device_control(lv_lcd, FBIO_CURSOR, &touch_info);
    tx = touch_info.xres;
    ty = touch_info.yres;
    tstate = touch_info.touch_state;
}

/* Will be called by the library to read the touchpad */
static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) {
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
        //printf("last_x=%d:last_y=%d\n", last_x, last_y);
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;

    

    /*Return `false` because we are not buffering and no more data to read*/
    return false;
}

/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
    /*Your code comes here*/
    rt_device_control(lv_lcd, FBIO_CURSOR, &touch_info);
    tx = touch_info.xres;
    ty = touch_info.yres;
    tstate = touch_info.touch_state;

    if((tstate == 0) || (touch_info.xres >= 800) || (touch_info.yres >= 480))
    {
        return false;
    }
    else
    {
        return true;
    }
}

/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
    /*Your code comes here*/
    rt_device_control(lv_lcd, FBIO_CURSOR, &touch_info);
    tx = touch_info.xres;
    ty = touch_info.yres;
    //lcd_var_info.xres, lcd_var_info.yres
    tstate = touch_info.touch_state;
    (*x) = (tx * lcd_var_info.xres / 800);//lcd_var_info.xres - (tx * lcd_var_info.xres / 800);
    (*y) = (ty * lcd_var_info.yres / 480);//lcd_var_info.yres - (ty * lcd_var_info.yres / 480);
}

//(struct _disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
//static void  fbdev_flush(struct _disp_drv_t * disp_drv, const lv_area_t * area, const lv_color_t *color_p);
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
void rtt_lcd_register(void)
{
    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX);    /*Initialize the display buffer*/

    lv_disp_drv_t disp_drv;      /*Descriptor of a display driver*/

    //lv_disp_drv_t
    lv_disp_drv_init(&disp_drv); /*Basic initialization*/
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;
    /*Set up the functions to access to your display*/
    disp_drv.flush_cb = disp_flush;
    //disp_drv.flush_cb = fbdev_flush; /*Set your driver function*/
    disp_drv.buffer = &disp_buf; /*Assign the buffer to the display*/

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

void fbdev_init(void)
{
    // Open the file for reading and writing
    lv_lcd = rt_device_find(FBDEV_NAME);
    if (lv_lcd == RT_NULL)
    {
        rt_kprintf("can't find the lcd:%s\n", FBDEV_NAME);
        return;
    }
    rt_kprintf("\nThe framebuffer device was opened successfully.\n");
    rt_device_open(lv_lcd, RT_DEVICE_OFLAG_RDWR);
    // Get fixed screen information
    // git(len start) 
    rt_device_control(lv_lcd, FBIOGET_FSCREENINFO, &lcd_info);
    //bits_per_pixel xres yres
    rt_device_control(lv_lcd, FBIOGET_VSCREENINFO, &lcd_var_info);

    rt_kprintf("\n%dx%d, %dbpp\n", lcd_var_info.xres, lcd_var_info.yres, lcd_var_info.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize = lcd_var_info.xres * lcd_var_info.yres * lcd_var_info.bits_per_pixel / 8;
    rtt_fbp = (lv_color_int_t *)rt_malloc(lcd_var_info.xres * lcd_var_info.yres * 4);//(lv_color_int_t *)fbp;

    // Map the device to memory
    fbp = (rt_uint8_t *)((lcd_info).smem_start);
    rt_kprintf("\nThe framebuffer device was mapped to memory successfully.\n");
}

#define RGB888_TO_RGB565(r, g, b)    ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
#define RGB332_TO_RGB565(r, g, b)    ((((r) << 2) << 11) | (((g) << 3) << 5) | ((b) << 2))

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
//static void fbdev_flush(struct _disp_drv_t * disp_drv, const lv_area_t * area, const lv_color_t *color_p)
{
    rt_uint32_t x1 = 0, x2 = 0, y1 = 0, y2 = 0;
    long int location = 0;
    uint32_t x;
    uint32_t y;
    x1 = area->x1;
    x2 = area->x2;
    y1 = area->y1;
    y2 = area->y2;
    //rt_kprintf("aaaaa\n");
    /*Return if the area is out the screen*/
    if (x2 < 0)
        return;
    if (y2 < 0)
        return;
    if (x1 > lcd_var_info.xres - 1)
        return;
    if (y1 > lcd_var_info.yres - 1)
        return;

    /*Truncate the area to the screen*/
    int32_t act_x1 = x1 < 0 ? 0 : x1;
    int32_t act_y1 = y1 < 0 ? 0 : y1;
    int32_t act_x2 = x2 > lcd_var_info.xres - 1 ? lcd_var_info.xres - 1 : x2;
    int32_t act_y2 = y2 > lcd_var_info.yres - 1 ? lcd_var_info.yres - 1 : y2;

    //rt_tick_t tick1 = rt_tick_get();

    for (y = act_y1; y <= act_y2; y++)
    {
        for (x = act_x1; x <= act_x2; x++)
        {
            location = (x) + (y)*lcd_var_info.xres;
            rtt_fbp[location] = color_p->full;
            color_p++;
        }

        color_p += x2 - act_x2;
    }
    //rt_tick_t tick2 = rt_tick_get();
    rt_memcpy((void *)fbp, rtt_fbp, lcd_var_info.xres * lcd_var_info.yres * 4);
    //rt_tick_t tick3 = rt_tick_get();
    //rt_memcpy(fbp, )
    //rt_device_control(lv_lcd, RTGRAPHIC_CTRL_RECT_UPDATE, &lcd_info);
    lv_disp_flush_ready(disp_drv); 
    //printf("%d:%d:%d\n", tick2 - tick1, tick3 - tick2, rt_tick_get() - tick3);
}
